/*
Author: Marek Skrajnowski
Date: 09.11.2011

Handle code from lecturer materials.
*/

#ifndef HANDLE_H
#define HANDLE_H

#include <cassert>

typedef unsigned int bufferId_t;
typedef unsigned int dataId_t;

template<size_t BufferBits = 8, size_t CorrectionBits = 8> 
class Handle {
public:
	enum {
		// sizes to use for bit fields
		MAX_BITS_BUFFER_INDEX = BufferBits,
		MAX_BITS_MAGIC = CorrectionBits,
		MAX_BITS_DATA_INDEX = 32 - BufferBits - CorrectionBits,
				
		// sizes to compare against for assering dereferences
		MAX_BUFFER_INDEX = (1 << MAX_BITS_BUFFER_INDEX) - 1,
		MAX_DATA_INDEX = (1 << MAX_BITS_DATA_INDEX) - 1,
		MAX_MAGIC = (1 << MAX_BITS_MAGIC) - 1,
	};

	union {		

		struct {
			bufferId_t m_BufferIndex: MAX_BITS_BUFFER_INDEX;
			dataId_t m_DataIndex: MAX_BITS_DATA_INDEX;
			unsigned int m_Magic: MAX_BITS_MAGIC;
		};

		unsigned int m_Handle;
	};

public:
	Handle(void): m_Handle(0) {}
	Handle(const Handle& other): m_Handle(other.m_Handle) {}
	
	bufferId_t getBufferIndex() const { return m_BufferIndex; }
	dataId_t getDataIndex() const { return m_DataIndex; }
	unsigned int getMagic() const { return m_Magic; }
	unsigned int getHandle() const { return m_Handle; }
	bool isNull() const { return !m_Handle; }

	operator unsigned int (void) const { return m_Handle; }

	void init(bufferId_t bufferIndex, dataId_t dataIndex) {
			
		assert(isNull()); // dont allow reassignment
		assert(bufferIndex <= MAX_BUFFER_INDEX); // verify range
		assert(dataIndex <= MAX_DATA_INDEX); // verify range

		static unsigned int s_AutoMagic = 0;
		if(++s_AutoMagic > MAX_MAGIC) {
			s_AutoMagic = 1; // 0 is used for "null handle"
		}

		m_BufferIndex = bufferIndex;
		m_DataIndex = dataIndex;
		m_Magic = s_AutoMagic;
	}

	Handle& operator= (const Handle& other) {
		m_Handle = other.m_Handle;
		return *this;
	}

	bool operator != ( const Handle& other ) {
		return ( getHandle() != other.getHandle() );
	}

	bool operator == ( const Handle& other ) {
		return ( getHandle() == other.getHandle() );
	}
};

#endif